﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using BrightIdeasSoftware;
using HML.Design;

namespace HML.SkinTool
{
    public class TreeListViewPropertyGrid : BrightIdeasSoftware.TreeListView, IServiceProvider, IWindowsFormsEditorService
    {
        #region 事件

        public delegate void PropertyValueChangedHandler(object sender, TreeListViewPropertyGridValueChangedEventArgs e);
        private static readonly object EventPropertyValueChanged = new object();
        /// <summary>
        /// 编辑完成后事件
        /// </summary>
        public event PropertyValueChangedHandler PropertyValueChanged
        {
            add { Events.AddHandler(EventPropertyValueChanged, value); }
            remove { Events.RemoveHandler(EventPropertyValueChanged, value); }
        }

        #endregion

        #region 属性

        /// <summary>
        /// 编辑框的rect
        /// </summary>
        public Rectangle SubItemBounds { get; set; }

        #endregion

        #region 字段

        /// <summary>
        /// 是否允许切换成编辑框
        /// </summary>
        bool isAllowTextEdit = true;

        #endregion

        public TreeListViewPropertyGrid() : base()
        {
            this.CanExpandGetter = x => (x as TreeListViewPropertyGridItem).Children.Count > 0;
            this.ChildrenGetter = x => (x as TreeListViewPropertyGridItem).Children;
        }

        #region 重写

        protected override void OnCellClick(CellClickEventArgs args)
        {
            base.OnCellClick(args);

            if (args.Model != null && args.Column.AspectName == "PropertyDisplayValue" && ((TreeListViewPropertyGridItem)args.Model).IsAllowEdit && new Rectangle(args.SubItem.Bounds.Right - args.SubItem.Bounds.Height - 1, args.SubItem.Bounds.Y, args.SubItem.Bounds.Height + 2, args.SubItem.Bounds.Height).Contains(args.Location))
            {
                this.SubItemBounds = args.SubItem.Bounds;
                EditorAttribute editorAttribute = null;
                if (((TreeListViewPropertyGridItem)args.Model).PropertyInfo.IsDefined(typeof(EditorAttribute), true))
                {
                    editorAttribute = ((EditorAttribute)((TreeListViewPropertyGridItem)args.Model).PropertyInfo.GetCustomAttributes(typeof(EditorAttribute), true)[0]);
                }
                else
                {
                    editorAttribute = ((EditorAttribute)((TreeListViewPropertyGridItem)args.Model).PropertyInfo.PropertyType.GetCustomAttributes(typeof(EditorAttribute), true)[0]);
                }
                Type editorType = Type.GetType(editorAttribute.EditorTypeName);
                if (editorType != null)
                {
                    TreeListViewPropertyGridItem item = (TreeListViewPropertyGridItem)args.Model;
                    PropertyDescriptor descriptor = TypeDescriptor.GetProperties(((TreeListViewPropertyGridItem)args.Model).PropertyOwnerObject)[((TreeListViewPropertyGridItem)args.Model).PropertyInfo.Name];
                    UITypeEditor editor = (UITypeEditor)Activator.CreateInstance(editorType);

                    object old_value = item.PropertyValue;
                    object new_value = editor.EditValue(new TreeListViewPropertyGridTypeDescriptorContext(item.PropertyValue, descriptor), this, old_value);
                    if (old_value != new_value)
                    {
                        item.PropertyValue = new_value;
                        item.PropertyDisplayValue = TypeDescriptor.GetConverter(item.PropertyInfo.PropertyType).ConvertTo(item.PropertyValue, typeof(string)) as string;
                        this.RefreshObject(item);

                        this.OnPropertyValueChanged(new TreeListViewPropertyGridValueChangedEventArgs(item));
                    }
                }

                this.isAllowTextEdit = false;
            }
        }

        protected override void OnCellEditStarting(CellEditEventArgs e)
        {
            base.OnCellEditStarting(e);

            if (!((TreeListViewPropertyGridItem)e.RowObject).IsAllowEdit)
            {
                e.Cancel = true;
                return;
            }

            if (!this.isAllowTextEdit)
            {
                this.isAllowTextEdit = true;
                e.Cancel = true;
                return;
            }

            if (e.Column.AspectName == "PropertyDisplayValue")
            {
                TextBox textBox = (TextBox)e.Control;
                textBox.AutoSize = false;
                textBox.Size = new Size(e.CellBounds.Size.Width - e.CellBounds.Size.Height - 6, e.CellBounds.Size.Height);
                textBox.Location = new Point(e.CellBounds.Location.X + 1, e.CellBounds.Location.Y + 1);
                Point point = this.PointToClient(Control.MousePosition);
                textBox.Select(textBox.GetCharIndexFromPosition(new Point(point.X - e.CellBounds.X, point.Y - e.CellBounds.Y)), 0); ;
            }
        }

        protected override void OnCellEditFinished(CellEditEventArgs e)
        {
            base.OnCellEditFinished(e);

            if (e.Value.ToString() != e.NewValue.ToString())
            {
                TreeListViewPropertyGridItem item = (TreeListViewPropertyGridItem)e.RowObject;
                try
                {
                    item.PropertyValue = TypeDescriptor.GetConverter(typeof(Color)).ConvertFrom(e.NewValue);
                    item.PropertyDisplayValue = (string)e.NewValue;
                    this.OnPropertyValueChanged(new TreeListViewPropertyGridValueChangedEventArgs(item));
                }
                catch
                {
                    e.NewValue = e.Value;
                    item.PropertyDisplayValue = (string)e.Value;
                    e.Cancel = true;
                }

            }
        }

        protected override void OnDrawSubItem(DrawListViewSubItemEventArgs args)
        {
            base.OnDrawSubItem(args);

            if (((OLVColumn)args.Header).AspectName == "PropertyName")
            {
                TreeListViewPropertyGridItem item = (TreeListViewPropertyGridItem)(((BrightIdeasSoftware.OLVListItem)args.Item).RowObject);
                if (item.EnvironmentProperty)
                {
                    Rectangle rect = new Rectangle(args.Bounds.Right - args.Bounds.Height - 1, args.Bounds.Y + 2, args.Bounds.Height - 5, args.Bounds.Height - 5);

                    float min_grid = rect.Size.Width / 10f;
                    PointF[] check_char_line = new PointF[] {
                        new PointF(rect.X+min_grid,rect.Y+ min_grid * 4.5f),
                        new PointF(rect.X+min_grid *4,rect.Y+  min_grid * 7),
                        new PointF(rect.X+min_grid*8f ,rect.Y+  min_grid*2)
                        };

                    SmoothingMode smoothingMode = args.Graphics.SmoothingMode;
                    args.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

                    Pen check_fore_pen = new Pen(Color.OliveDrab, rect.Size.Width / 7f);
                    args.Graphics.DrawLines(check_fore_pen, check_char_line);
                    check_fore_pen.Dispose();

                    args.Graphics.SmoothingMode = smoothingMode;

                }

            }
            else if (((OLVColumn)args.Header).AspectName == "PropertyDisplayValue")
            {
                Rectangle rect = new Rectangle(args.SubItem.Bounds.Right - args.SubItem.Bounds.Height - 1, args.SubItem.Bounds.Y + 2, args.SubItem.Bounds.Height - 5, args.SubItem.Bounds.Height - 5);

                TreeListViewPropertyGridItem item = (TreeListViewPropertyGridItem)(((BrightIdeasSoftware.OLVListItem)args.Item).RowObject);
                if (item.IsAllowEdit)
                {
                    if (item.EditorAttribute.EditorTypeName == typeof(System.Drawing.Design.ColorEditor).AssemblyQualifiedName)
                    {
                        Pen pen = new Pen(Color.Black);
                        SolidBrush sb = new SolidBrush((Color)item.PropertyValue);
                        args.Graphics.FillRectangle(sb, rect);
                        args.Graphics.DrawRectangle(pen, rect);
                        sb.Dispose();
                        pen.Dispose();
                    }
                    else if (item.EditorAttribute.EditorTypeName == typeof(HML.Design.AlphaColorEditor).AssemblyQualifiedName)
                    {
                        Color color = (Color)(Color)item.PropertyValue;
                        Pen pen = new Pen(Color.Black);
                        SolidBrush solidBrush1 = new SolidBrush((color == Color.Empty || color == Color.Transparent) ? Color.Empty : Color.FromArgb(color.R, color.G, color.B));
                        SolidBrush solidBrush2 = new SolidBrush(color);
                        args.Graphics.FillRectangle(solidBrush1, new Rectangle(rect.X, rect.Y, rect.Width / 2, rect.Height));
                        if (!(color == Color.Empty || color == Color.Transparent))
                            args.Graphics.DrawLine(pen, rect.X + rect.Width / 2 + 1, rect.Y, rect.X + rect.Width / 2 + 1, rect.Bottom - 2);
                        args.Graphics.FillRectangle(solidBrush2, new Rectangle(rect.X + rect.Width / 2, rect.Y, rect.Width / 2, rect.Height));
                        args.Graphics.DrawRectangle(pen, rect);
                        solidBrush1.Dispose();
                        solidBrush2.Dispose();
                        pen.Dispose();
                    }
                }
            }
        }

        #endregion

        #region 虚方法

        protected virtual void OnPropertyValueChanged(TreeListViewPropertyGridValueChangedEventArgs e)
        {
            PropertyValueChangedHandler handler = Events[EventPropertyValueChanged] as PropertyValueChangedHandler;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        #endregion

        #region IServiceProvider

        public object GetService(Type serviceType)
        {
            if (serviceType == typeof(IWindowsFormsEditorService))
            {
                return this;
            }
            return null;
        }

        #endregion

        #region IWindowsFormsEditorService

        private TreeListViewPropertyGridEditorServiceForm editDialogForm = null;

        public void CloseDropDown()
        {
            this.editDialogForm.Close();
            this.editDialogForm.Dispose();
        }

        public void DropDownControl(Control control)
        {
            this.editDialogForm = new TreeListViewPropertyGridEditorServiceForm();
            this.editDialogForm.Size = new Size(control.Size.Width + (this.editDialogForm.Bounds.Size.Width - this.editDialogForm.ClientSize.Width), control.Size.Height + (this.editDialogForm.Bounds.Size.Height - this.editDialogForm.ClientSize.Height));
            this.editDialogForm.Controls.Add(control);
            Rectangle screen_rect = Screen.FromControl(this).WorkingArea;
            Point screen_control_point = this.PointToScreen(new Point(this.SubItemBounds.Right - control.Size.Width, this.SubItemBounds.Bottom));
            if (screen_control_point.Y + control.Size.Height > screen_rect.Bottom)
            {
                screen_control_point = this.PointToScreen(new Point(this.SubItemBounds.Right - control.Size.Width, this.SubItemBounds.Y - control.Size.Height));
            }

            this.editDialogForm.Location = screen_control_point;
            this.editDialogForm.ShowDialog();
        }

        public DialogResult ShowDialog(Form dialog)
        {
            throw new NullReferenceException("未实现");
        }

        #endregion

        public class TreeListViewPropertyGridTypeDescriptorContext : ITypeDescriptorContext
        {
            public IContainer Container
            {
                get { return null; }
            }

            private readonly object instance;
            public object Instance
            {
                get { return this.instance; }
            }

            private readonly PropertyDescriptor propertyDescriptor;
            public PropertyDescriptor PropertyDescriptor
            {
                get { return this.propertyDescriptor; }
            }

            private TreeListViewPropertyGrid owner;

            public TreeListViewPropertyGridTypeDescriptorContext(object instance, PropertyDescriptor propertyDescriptor)
            {
                this.instance = instance;
                this.propertyDescriptor = propertyDescriptor;
            }

            public TreeListViewPropertyGridTypeDescriptorContext(TreeListViewPropertyGrid owner, object instance, PropertyDescriptor propertyDescriptor) : this(instance, propertyDescriptor)
            {
                this.owner = owner;
            }

            public void OnComponentChanged()
            {
                // 处理组件更改事件
            }

            public bool OnComponentChanging()
            {
                // 处理组件更改前事件
                return true;
            }

            public object GetService(Type serviceType)
            {
                if (serviceType == typeof(IServiceProvider))
                {
                    return this.owner;
                }
                return null;
            }
        }

        public class TreeListViewPropertyGridEditorServiceForm : Form
        {
            public TreeListViewPropertyGridEditorServiceForm() : base()
            {
                this.ShowInTaskbar = false;
                this.MinimizeBox = false;
                this.MaximizeBox = false;
                this.Text = "";
                this.FormBorderStyle = FormBorderStyle.FixedToolWindow;
                this.AutoScaleMode = AutoScaleMode.None;
                this.StartPosition = FormStartPosition.Manual;
            }
            protected override void WndProc(ref Message m)
            {
                const int WM_ACTIVATE = 0x0006;
                const int WA_INACTIVE = 0x0000;
                if (m.Msg == WM_ACTIVATE)
                {
                    int wParam = (int)m.WParam & 0xFFFF;
                    if (wParam == WA_INACTIVE)
                    {
                        this.Close();
                        this.Dispose();
                    }
                }

                base.WndProc(ref m);
            }
        }


    }


}
